# Copyright (c) 2019 Presto Labs Pte. Ltd.
# Author: leon

import logging
from coin.base.book import BookBuilderBase
from coin.exchange.bybit.kr_rest.futures_product import BybitFuturesProduct


class BookEntry(object):
  def __init__(self, price, qty):
    self.price = float(price)
    self.qty = float(qty)


class BookBuilder(BookBuilderBase):
  ProductType = BybitFuturesProduct

  def __init__(self, native_symbol, book_builder_observer=None):
    super().__init__("Bybit", native_symbol, book_builder_observer=book_builder_observer)

  def snapshot(self, data: list):
    for item in data:
      price = item['price']
      qty = item['size']
      side = item['side']
      price = float(price)
      qty = float(qty)
      if side == 'Buy':
        self._insert_bid(price, qty, insert_only=True)
      elif side == 'Sell':
        self._insert_ask(price, qty, insert_only=True)
      else:
        raise ValueError(data)

  def update(self, data: dict):
    for type, items in data.items():
      if type not in ['delete', 'update', 'insert']:
        continue
      for item in items:
        price = item['price']
        qty = item.get('size', None)
        side = item['side']
        price = float(price)
        if qty is not None:
          qty = float(qty)
        if type == 'delete':
          if side == 'Buy':
            self._delete_bid(price)
          elif side == 'Sell':
            self._delete_ask(price)
        elif type in ['update', 'insert']:
          if side == 'Buy':
            self._insert_bid(price, qty)
          elif side == 'Sell':
            self._insert_ask(price, qty)
        else:
          raise ValueError(data)

  def _insert_bid(self, price, qty, insert_only=False):
    assert price > 0
    assert qty > 0
    entry = BookEntry(price, qty)
    if insert_only:
      assert price not in self._bid_price_map
    self._bid_price_map[price] = entry

    if self.ask0() is None:
      return
    bid_price = self.bid0().price
    while self.ask0() is not None and bid_price >= self.ask0().price:
      ask_price = self.ask0().price
      self._delete_ask(ask_price)
      logging.debug('REMOVE CX ASK')

  def _insert_ask(self, price, qty, insert_only=False):
    assert price > 0
    assert qty > 0
    entry = BookEntry(price, qty)
    if insert_only:
      assert price not in self._ask_price_map
    self._ask_price_map[price] = entry

    if self.bid0() is None:
      return
    ask_price = self.ask0().price
    while self.bid0() is not None and ask_price <= self.bid0().price:
      bid_price = self.bid0().price
      self._delete_bid(bid_price)
      logging.debug('REMOVE CX BID')

  def _delete_bid(self, price):
    if price in self._bid_price_map:
      del self._bid_price_map[price]
    else:
      logging.debug('DEL_BID_ERROR: %s', price)

  def _delete_ask(self, price):
    if price in self._ask_price_map:
      del self._ask_price_map[price]
    else:
      logging.debug('DEL_ASK_ERROR: %s', price)
